/**
 * The jQuery namespace.
 * @external "jQuery"
 * @see {@link http://learn.jquery.com|jQuery}
 */

/**
 * @namespace UCD
 */
var UCD = UCD || {
    Core: jQuery
};

/**
 * @typedef {Object} CommonChartDataItem
 *
 * 图表数据格式是[ { value: [group_0_i, group_1_i, group_3_i] } ]，group_#表示第#组数据的第i个值。例如：
 * 
 *     [ { value: [10, 20, 30] } , { value: [40, 50, 60] } , { value: [70, 80, 90] } ]
 * 
 * @property {Number[]} value 每组数据第i个数据的值
 * @property {String[]} [label] 每组数据第i个数据的扩展字段
 */

(function(ucd, $) {

    var RE_PATH_VALUES = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig;
    var RE_PATH_CMDS = /([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig;
    var PATH_CMD_PARAMS_COUNT = {
        a: 7,
        c: 6,
        h: 1,
        l: 2,
        m: 2,
        r: 4,
        q: 4,
        s: 4,
        t: 2,
        v: 1,
        z: 0
    };

    // Simple Timers Manager
    function TimerManager() {
        if (!(this instanceof TimerManager)) {
            return new TimerManager();
        }

        this.timeouts = [];
        this.intervals = [];

        this.setTimeout = function(callback, delay) {
            var timer = setTimeout(callback, delay);
            delay > 0 ? this.timeouts.push(timer) : undefined;
            return timer;
        };

        this.setInterval = function(callback, delay) {
            var timer = setInterval(callback, delay);
            delay > 0 ? this.intervals.push(timer) : undefined;
            return timer;
        };

        function indexOf(a, val) {
            if (a.indexOf) {
                return a.indexOf(val);
            }

            // TODO: IE8 lack of indexOf
            for (var i = 0, len = a.length; i < len; i++) {
                if (a[i] === val) {
                    return i;
                }
            }

            return -1;
        }

        var clear = function(timers, fn, timer) {
            var found = indexOf(timers, timer);
            if (found !== -1) {
                fn(timers[found]);
                timers.splice(found, 1);
            }
        };

        this.clearTimeout = function(timer) {
            clear(this.timeouts, window.clearTimeout, timer);
        };

        this.clearInterval = function(timer) {
            clear(this.intervals, window.clearInterval, timer);
        };

        this.clearTimeouts = function() {
            TimerManager.each(this.timeouts, window.clearTimeout);
        };

        this.clearIntervals = function() {
            TimerManager.each(this.intervals, window.clearInterval);
        };

        this.clearAll = function() {
            this.clearTimeouts();
            this.clearIntervals();
        };
    }

    TimerManager.each = function(a, callback) {
        while (a.length) { 
            callback(a.shift());
        }
    };

    ucd.TimerManager = TimerManager;

    // 支持requestAnimationFrame动画
    // https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
    // http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
    (function() {
        var lastTime = 0;
        var vendors = ['webkit', 'moz'];
        for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
            window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
            window.cancelAnimationFrame =
              window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
        }

        if (!window.requestAnimationFrame)
            window.requestAnimationFrame = function(callback, element) {
                var currTime = new Date().getTime();
                var timeToCall = Math.max(0, 16 - (currTime - lastTime));
                var id = window.setTimeout(function() { callback(currTime + timeToCall); },
                  timeToCall);
                lastTime = currTime + timeToCall;
                return id;
            };

        if (!window.cancelAnimationFrame)
            window.cancelAnimationFrame = function(id) {
                clearTimeout(id);
            };
    }());
    
    /**
     * 坐标轴
     *
     * TODO: mainCord与yCord1的宽度必须相同
     * 
     * @smartueExample 基本用法 charts/cords/basic.html
     * @smartueExample 设置风格 charts/cords/setStyle.html
     *
     * 
     * @class Cords
     * @memberOf UCD
     * @requires jQuery
     * @requires UCD.getColor
     * 
     * @param {String|Object} container - jQuery selector
     */
    ucd.Cords = function(container) {
        this._settings = {
            container: container,
            root: $("<div class='Cords'></div>"),
            mainCord: $("<div class='mainCord'></div>"),
            cordContent: $("<div class='cordContent'></div>"),
            cordText: $("<div class='cordText'></div>"),
            cordRule: $("<div class='cordRule'></div>"),
            yCord1: $("<div class='yCord1'></div>"),
            yCord2: $("<div class='yCord2'></div>"),
            grid: $("<div class='grid'></div>"),
            gridY: $("<div class='grid gridY'></div>"),
            y1CordText: $("<div class='cordText'></div>"),
            y1CordRule: $("<div class='cordRule'></div>"),
            y2CordText: $("<div class='cordText'></div>"),
            y2CordRule: $("<div class='cordRule'></div>"),
            legend: null,
            scrollLeft: 0,
            totalRatio: 0.95, //实际容器大小与总容器大小比例0.95:1
            isYRuleInterlay: false, //Y轴刻度间隔显示
            isNoBeyond: true, //是否让图表不超出最顶边
            isXRule: true, //是否显示X轴刻度
            isYRule: true, //是否显示Y轴刻度
            isXRuleLine: true, //是否显示X轴标尺
            isYRuleLine: true, //是否显示Y轴标尺
            isLineFeed: "false", //是否换行显示
            defaultColor: "#52CCA3", //默认颜色
            zeroPosition: 0, //如果为负值计算0所在坐标位置(柱状负值情况)
            padding: 0 //图表边距
        };
        this.SVG_NS = "http://www.w3.org/2000/svg";
        this.XLINK_NS = "http://www.w3.org/1999/xlink";

        var settings = this._settings;
        settings.legendArray = []; //存储图例
        settings.V = ucd.V = { // TODO: bad idea using global V.NS
            NS: "v"
        };
        settings.mainCord.append(settings.cordContent);
        settings.cordContent.append(settings.cordText, settings.cordRule);
        $(settings.container).append(settings.root);

        _chartReady(this, settings);
        var isPad = ("ontouchstart" in window) ? true : false;
        settings.Source = {
            isPad: isPad,
            MOUSEDOWN: isPad ? "touchstart" : "mousedown",
            MOUSEMOVE: isPad ? "touchmove" : "mousemove",
            MOUSEUP: isPad ? "touchend" : "mouseup",
            CLICK: isPad ? "touchend" : "click"
        }
    }

    function _chartReady(self, settings) {
        if (!self.isSVG() && !window._chartReady) {
            if (document.namespaces) {
                document.namespaces.add(settings.V.NS, "urn:schemas-microsoft-com:vml");
                var head = document.getElementsByTagName("head")[0]; // $(document.getElementsByTagName("head")[0]);
                var styleRules = '{ BEHAVIOR: url(#default#VML); position: absolute; display:inline-block; }';
                var style = document.createElement("style");
                style.type = "text/css";

                style.styleSheet.cssText = $.map(['*', 'shape', 'oval', 'line', 'rect', 
                    'shadow', 'group', 'roundrect', 
                    'skew', 'fill', 'stroke', 'text', 'textpath', 
                    'textbox', 'vmlframe', 'clippath', 'path'], function(v) {
                    return settings.V.NS + "\\:" + v + styleRules;
                }).join(' ');
                
                head.appendChild(style);
            }

            window._chartReady = true;
        }
    }

    //获取文本的最大宽高
    function _calMaxItemSize(settings, cord, isVertical) {
        var max = 0;
        var w = cord.outerWidth(true) - cord.width();
        cord.find(".dot").each(function(i) {
            var size = settings.isLineFeed == true ? $(this).outerHeight(true) : $(this).outerHeight();
            if (isVertical) {
                size = settings.isLineFeed == true ? $(this).outerWidth(true) : $(this).outerWidth();
            }
            if (max < size) {
                max = size;
            }
        });
        max = max > 0 ? max + w : max;
        return max;
    }

    function _getLegendHeight(settings) {
        var legendHeight = 0;
        if (settings.legend) {
            legendHeight = settings.legend.height();
        }
        return legendHeight;
    }

    function _setSizeMainCord(settings, size, isFromYCord1) {
        var legendHeight = _getLegendHeight(settings);
        var plus = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
        if (!settings.orientation) {
            settings.mainCord.height(size).width("100%");
            settings.cordContent.height(size).width("100%");
        } else {
            var h = settings.container.height() - legendHeight - plus;
            settings.mainCord.width(size).height(h);
            settings.cordContent.width(size).height(h);
        }
    }

    /**
     * set Main Cord pos
     * 
     * @param {Object} settings settings
     * @param {Object} dot      dot
     * @param {Number} value    value
     * @param {Object} rule     rule object
     */
    function _setPosMainCord(settings, dot, value, rule) {
        var d = rule ? rule : dot;
        var legendHeight = _getLegendHeight(settings);
        if (!settings.orientation) {
            var bottom = settings.isRuleMoveUp ? 0 : "";
            dot.css({
                "left": value - d.outerWidth(true) * 0.5,
                "top": "",
                "right": "",
                "bottom": bottom,
                "opacity": 1,
                "marginTop": ""
            });
            settings.mainCord.css("bottom", legendHeight);
            //X轴文本换行
            if (settings.isLineFeed == true) {
                var index = dot.prevAll(".dot").length;
                if (index % 2 != 0) {
                    dot.css("marginTop", dot.height());
                }
            }
            if (rule && rule.text() != "") {
                var height = settings.isRuleMoveUp ? 10 : 5;
                var paddingBottom = settings.isRuleMoveUp ? "20px" : "0px";
                dot.css({
                    "left": value,
                    "height": height
                });
                rule.css({
                    "padding-top": "8px",
                    "padding-bottom": paddingBottom,
                    "right": ""
                });
                if (settings.isLineFeed == true) {
                    var index = rule.prevAll(".dot").length;
                    if (index % 2 != 0) {
                        dot.css("height", rule.height() * 2 - 10);
                    }
                }
            }
        } else {
            dot.css({
                "top": value - d.outerHeight() * 0.5,
                "left": 0,
                "opacity": 1,
                "marginTop": 0
            });
            settings.mainCord.css({
                "bottom": "",
                "left": 0
            });
            if (rule) {
                var size = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
                dot.css("top", value).css("left", size - dot.width()).css("marginTop", 0);
                rule.css({
                    "right": "5px",
                    "left": "auto"
                });
            }
        }
    }

    function _transValue(settings, value, max, total, min, firstNum) {
        min = min ? min : 0;
        firstNum = firstNum ? firstNum : 0;

        if (Math.abs(max - firstNum) < 1e-6) {
            return 0;
        }

        return Math.round(total * settings.totalRatio * (value - firstNum + Math.abs(min)) / (max - firstNum));
    }

    function _getTall(settings, data, max, isVertical, min, firstNum) {
        var tmp = [];
        var container = settings.container;
        if (settings.isComPlex) {
            container = settings.comPlexContainer;
        }
        var total = container.height();
        var plus = _calMaxItemSize(settings, settings.mainCord, isVertical);
        if (isVertical) {
            total = container.width();
            var yCord2 = _calMaxItemSize(settings, settings.yCord2, isVertical);
            total -= yCord2;
        }

        total -= plus;
        var legendHeight = 0;
        if (settings.legend && !isVertical) {
            legendHeight = settings.legend.height();
            total -= legendHeight;
        }
        var h = isVertical ? _calMaxItemSize(settings, settings.yCord1, isVertical) : plus;
        settings.totalNum = Math.round((total) - (total * settings.totalRatio)); //1-0.95比例的高度

        for (var i = 0; i < data.length; i++) {
            var tall = _transValue(settings, data[i], max, total, min, firstNum);
            var tallEnd = tall;
            if (!isVertical) {
                tallEnd = total - tall;
            }
            if (isVertical)
                tallEnd += plus;
            tmp.push(tallEnd);
        }
        if (min < 0) {
            settings.zeroPosition = _transValue(settings, 0, max, total, min, firstNum);
        }
        return tmp;
    }

    function _setPosYCord1(settings, dot, value, rule, isYRuleInterlay, data1) {
        var d = rule ? rule : dot;
        var legendHeight = _getLegendHeight(settings);
        if (settings.orientation) {
            var plus = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
            dot.css({
                "left": parseInt(value - plus - d.outerWidth(true) * 0.5),
                "top": "",
                "opacity": 1
            });
            settings.yCord1.width("100%").css("bottom", legendHeight);
            if (rule) {
                dot.css("left", value - plus);
            }
            if (isYRuleInterlay) {
                var xSpaceWidth = data1[0] - data1[1];
                dot.css("left", value - plus - xSpaceWidth / 2);
            }
        } else {
            dot.css({
                "top": parseInt(value - d.outerHeight() * 0.5),
                "opacity": 1
            });
            settings.yCord1.height("100%").css({
                "bottom": "",
                "left": 0
            }).css("left", 0);
            if (rule) {
                var size = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
                dot.css("top", value - 1).css("left", size - dot.width());
                rule.css({
                    "right": "5px",
                    "left": "auto"
                });
                if (isYRuleInterlay) {
                    var xSpaceWidth = data1[0] - data1[1];
                    dot.css("top", value - xSpaceWidth / 2);
                }
            }
        }
    }

    function _setSizeYCord1(settings, size) {
        var legendHeight = _getLegendHeight(settings);
        var plus = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
        var total = settings.container.height();
        if (settings.orientation) {
            total = settings.container.width();
            if (settings.isYRuleInterlay) {
                var poses = _getMainPoses(settings, 1);
                settings.yCord1.width(total - plus).css({
                    "left": plus,
                    "top": poses[0] + 1
                });
            } else {
                settings.yCord1.height(size).width(total - plus).css("left", plus);
            }
        } else {
            var w = settings.yCord1.outerWidth(true) - settings.yCord1.width();
            settings.yCord1.width(size - w).height(total - plus - legendHeight).css("left", 0);
        }
    }

    function _setPosYCord2(settings, dot, value, rule) {
        dot.css({
            "top": parseInt(value - dot.outerHeight() * 0.5),
            "right": 0,
            "opacity": 1
        });
        settings.yCord2.height("100%").css("bottom", "");
        if (rule) {
            dot.css({
                "left": 0,
                "rigth": "auto",
                "top": value - 1
            });
        }
    }

    function _setSizeYCord2(settings, size) {
        var legendHeight = _getLegendHeight(settings);
        var plus = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
        if (settings.orientation) {
            plus = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
        }
        var total = settings.container.height();
        var w = settings.yCord1.outerWidth(true) - settings.yCord1.width();
        settings.yCord2.width(size - w).height(total - plus - legendHeight).css("right", 0);
    }

    //计算图元尺寸及间隔
    function _calSize(settings, datalen) {
        datalen = settings.initDataCount ? settings.initDataCount : datalen; //settings.initDataCount:初始数据条数，数据添加后进行刷新操作，保证每条数据间距与初始保持一致
        var container = settings.container;
        //如果当前创建模式为复合图当前容器有变化
        if (settings.isComPlex) {
            container = settings.comPlexContainer;
        }
        var w = container.outerWidth() - /*plus - */ _calMaxItemSize(settings, settings.yCord2, true);
        if (settings.orientation) { //计算size
            w = container.outerHeight(true);
            var legendHeight = _getLegendHeight(settings);
            w -= legendHeight;
        }
        var plus2 = settings.isComPlex ? 0 : _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
        w -= plus2;

        var len = (datalen == 1 && settings.isSide) ? datalen + 1 : datalen;
        var size = settings.isSide ? 2 * w / 5 / (len - 1) : 2 * w / 5 / (datalen + 0.5); //比值为2:3, 0.5是为了留半个item位置
        var blank = settings.isSide ? 3 * w / 5 / (len - 1) : 3 * w / 5 / (datalen + 0.5);
        return {
            size: (size),
            blank: (blank)
        }
    }

    function _relayoutMainCord(settings, datalen) {
        var legendHeight = _getLegendHeight(settings);
        var plus = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
        var size = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
        if (!settings.orientation) {
            var width = settings.container.width() - settings.yCord1.width() - settings.yCord2.width();
            settings.mainCord.css("left", plus).height(size).width(width);
            settings.cordContent.height(size);
        } else {
            var h = settings.container.height() - legendHeight - plus;
            settings.mainCord.height(h);
            settings.cordContent.height(h);
        }
        var sSize = _calSize(settings, datalen);
        settings.mainCord.find(".dot").each(function(i) {
            var index = parseInt($(this).attr("index"));
            var pos = settings.isSide ? parseInt((sSize.blank + sSize.size) * (index)) : parseInt((sSize.blank + sSize.size) * (index + 1) - sSize.size * 0.5);
            _setPosMainCord(settings, $(this), pos);
            if (settings.isXRule && $(this).text()) {
                _setPosMainCord(settings, settings.mainCord.find(".rule").eq(i), pos, $(this));
            }
            _setPosLegend(settings);
        });
    }

    function _getMainPoses(settings, datalen) {
        var tmp = [];
        var sSize = _calSize(settings, datalen);
        for (var i = 0; i < datalen; i++) {
            var pos = settings.isSide ? (sSize.blank + sSize.size) * (i) : (sSize.blank + sSize.size) * (i + 1) - sSize.size * 0.5;
            tmp.push(pos);
        }
        return tmp;
    }

    function _setPosLegend(settings) {
        if (settings.legend == null) {
            return;
        }
        var pos = 0;
        if (!settings.orientation) {
            //若无Y1轴，则与第一个图元左对齐。否则与Y1轴右边缘对齐
            if (settings.yCord1.find(".dot").length == 0) {
                pos = parseInt(settings.mainCord.get(0).style.left);
            } else {
                pos = _calMaxItemSize(settings, settings.yCord1, true);
            }
        } else {
            //与MainCord轴右边缘对齐
            pos = _calMaxItemSize(settings, settings.mainCord, true);
        }
        settings.legend.css("left", pos);
    }

    function _getDrawArea(settings, isVertical) {
        var w = 0,
            h = 0,
            plusw = 0,
            plush = 0;
        if (!settings.yCord1 || !settings.mainCord) {
            return {
                left: 0,
                right: 0,
                top: 0,
                bottom: 0
            };
        }
        if (!isVertical) {
            plush = settings.mainCord.outerHeight() - settings.mainCord.height();
            w = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
            var mainSize = settings.isRuleMoveUp ? 0 : _calMaxItemSize(settings, settings.mainCord, settings.orientation);
            h = _getLegendHeight(settings) + mainSize;
        } else {
            plusw = settings.mainCord.outerWidth() - settings.mainCord.width();
            plush = settings.yCord1.outerHeight() - settings.yCord1.height();
            w = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
            var mainSize = settings.isRuleMoveUp ? 0 : _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
            h = _getLegendHeight(settings) + mainSize;
        }
        return {
            left: w + plusw,
            bottom: h + plush,
            top: 0,
            right: _calMaxItemSize(settings, settings.yCord2, true)
        };
    }
    /*start ie兼容*/
    //将path处理成数组
    function _parsePathString(d) {
        if (!d) {
            return null;
        }
        var data = [];
        
        d.replace(RE_PATH_CMDS, function(a, b, c) {
            var params = [], cmd = b.toLowerCase();
            c.replace(RE_PATH_VALUES, function(a, b) {
                b && params.push(+b);
            });
            if (cmd == "m" && params.length > 2) {
                data.push([b].concat(params.splice(0, 2)));
                cmd = "l";
                b = b == "m" ? "l" : "L";
            }
            while (params.length >= PATH_CMD_PARAMS_COUNT[cmd]) {
                data.push([b].concat(params.splice(0, PATH_CMD_PARAMS_COUNT[cmd])));
                if (!PATH_CMD_PARAMS_COUNT[cmd]) {
                    break;
                }
            }
        });

        return data;
    }

    //处理Path兼容IE
    function _processPath(settings, path, d) {
        var nx, ny;
        if (!path) {
            return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
        }
        switch (path[0]) {
            case "M":
                d.X = path[1];
                d.Y = path[2];
                break;
            case "A":
                path = ["C"]["concat"](_a2c["apply"](0, [d.x, d.y]["concat"](path.slice(1))));
                break;
            case "L":
                path = ["C"]["concat"](_l2c(d.x, d.y, path[1], path[2]));
                break;
            case "Z":
                path = ["C"]["concat"](_l2c(d.x, d.y, d.X, d.Y));
                break;
        }
        return path;
    }

    var PI = Math.PI, RAD120 = PI * 120 / 180, mathCos = Math.cos, mathSin = Math.sin, 
        mathAsin = function(val) {
            return Math.asin( val.toFixed(9) );
        };

    function _a2c_calc(x1, y1, rx, ry, angle, largeArcFlag, sweepFlag, x2, y2) {
        var x = (x1 - x2) / 2,
            y = (y1 - y2) / 2, 
            h = (x * x) / (rx * rx) + (y * y) / (ry * ry), 
            rx2 = rx * rx,
            ry2 = ry * ry,
            factor = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
            cx = factor * rx * y / ry + (x1 + x2) / 2,
            cy = factor * -ry * x / rx + (y1 + y2) / 2,
            f1 = mathAsin( (y1 - cy) / ry ),
            f2 = mathAsin( (y2 - cy) / ry );

        if (x1 < cx) {
            f1 = PI - f1;
        }

        if (x2 < cx) {
            f2 = PI - f2;
        }

        if (sweepFlag) {
            if (f1 > f2) {
                f1 = f1 - PI * 2;
            }
        } else {
            if (f2 > f1) {
                f2 = f2 - PI * 2;
            }
        }

        return [f1, f2, cx, cy];
    }

    // TODO: 解决开源代码的问题
    function _a2c(x1, y1, rx, ry, angle, largeArcFlag, sweepFlag, x2, y2, recursive) {
        var fc = recursive ? recursive : _a2c_calc(x1, y1, rx, ry, angle, largeArcFlag, sweepFlag, x2, y2), 
            res = [], df = fc[1] - fc[0];

        if (Math.abs(df) > RAD120) {
            var f2Before = fc[1],
                x2Before = x2,
                y2Before = y2;
            fc[1] = fc[0] + RAD120 * (sweepFlag && fc[1] > fc[0] ? 1 : -1);
            x2 = fc[2] + rx * mathCos(fc[1]);
            y2 = fc[3] + ry * mathSin(fc[1]);
            res = _a2c(x2, y2, rx, ry, angle, 0, sweepFlag, x2Before, y2Before, [fc[1], f2Before, fc[2], fc[3]]);
        }
        df = fc[1] - fc[0];
        var t = Math.tan(df / 4),
            hx = 4 / 3 * rx * t,
            hy = 4 / 3 * ry * t,
            part2 = [2 * x1 - (x1 + hx * mathSin(fc[0])), 2 * y1 - (y1 - hy * mathCos(fc[0]))],
            part3 = [x2 + hx * mathSin(fc[1]), y2 - hy * mathCos(fc[1])],
            part4 = [x2, y2], 
            ret = [part2, part3, part4].concat(res);

        if (recursive) {
            return ret;
        } else {
            // convert array of array into array
            return ret.join().split(","); 
        }
    }

    function _l2c(x1, y1, x2, y2) {
        return [x1, y1, x2, y2, x2, y2];
    }
    /*end ie兼容*/

    var RE_OVAL = /^(r|rx|ry|cx|cy)$/i, OVAL_ATTR_PREFIX = ''; // 'data-';

    var ovalGetterAndSetter = {
        get: function(elem, key) {
            return parseFloat(elem[OVAL_ATTR_PREFIX + key]);
        }, 
        set: function(elem, key, value) {
            elem[key] = value;

            var cx, cy, r, rx, ry;
            cx = parseFloat(elem[OVAL_ATTR_PREFIX + 'cx']);
            cy = parseFloat(elem[OVAL_ATTR_PREFIX + 'cy']);
            r = parseFloat(elem[OVAL_ATTR_PREFIX + 'r']);
            if (!isNaN(r)) {
                rx = ry = r;
            } else {
                rx = parseFloat(elem[OVAL_ATTR_PREFIX + 'rx']);
                ry = parseFloat(elem[OVAL_ATTR_PREFIX + 'ry']);
            }

            if (!isNaN(cx) && !isNaN(cy) && !isNaN(rx) && !isNaN(ry)) {
                $(elem).css({
                    'left': cx - rx, 
                    'top': cy - ry, 
                    'width': rx * 2, 
                    'height': ry * 2
                });
            }

            return elem;
        }
    };

    /** @lends UCD.Cords.prototype */ 
    ucd.Cords.prototype = {
        constructor: ucd.Cords,
        /**
         * 是否支持SVG
         *
         * @private
         * 
         * @return {Boolean} true支持SVG，false不支持SVG
         */
        isSVG: function() {
            if (window.supportSVG !== undefined) {
                return window.supportSVG;
            }
            
            var svgDocumentTestElement = null;
            if (document.createElementNS) {
                svgDocumentTestElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
            } else {
                svgDocumentTestElement = document.createElement("svg");
                svgDocumentTestElement.setAttribute("xmlns", "http://www.w3.org/2000/svg");
            }
            window.supportSVG = !!svgDocumentTestElement.createSVGRect;
            return window.supportSVG;
        },
        /**
         * TODO：放到单独的位置实现，而不是Cords里面。
         * 
         * @return {Boolean} [description]
         */
        supportSMIL: function() {
            if (window.supportSMIL !== undefined) {
                return window.supportSMIL;
            }
            window.supportSMIL = !!document.createElementNS && /SVGAnimate/.test(Object.prototype.toString.call(document.createElementNS('http://www.w3.org/2000/svg', 'animate')));

            return window.supportSMIL;
        },

        /**
         * 创建渐变颜色
         * 
         * @param  {String} uid  unique id
         * @param  {String} s    渐变颜色描述字符串
         * @param  {Object} defs svg parent node, should be defs node.
         */
        createGradient: function(uid, s, defs) {
            var parts = s.replace(/\-/g, ' ').split(/\s+/), 
                radial = parts.length && parts[0] === 'r', 
                type = radial ? 'radialGradient' : 'linearGradient';

            var gradient, attrs, stops;

            // 0 0 100% 100%, red 0% 1, green 50% 1, blue 100% 1
            // r 50% 50% 50% 50% 50%, red 0% 1, green 50% 1, blue 100% 1
            if (radial) {
                attrs = {
                    "id": uid,
                    "cx": parts[1],
                    "cy": parts[2],
                    "r": parts[3],
                    "fx": parts[4],
                    "fy": parts[5]
                };

                stops = parts.slice(6);
            } else {
                attrs = {
                    "id": uid,
                    "x1": parts[0],
                    "y1": parts[1],
                    "x2": parts[2],
                    "y2": parts[3]
                };

                stops = parts.slice(4);
            }

            gradient = this.createElement(type, attrs, defs); 
            for (var i = 0, len = stops.length; i < len; i+=3) {
                this.createStop(gradient, stops[i], stops[i+1], stops[i+2]);
            }     
        }, 
        /**
         * 创建渐变颜色点
         * 
         * @param  {Object} root    svg parent node
         * @param  {String} color   color
         * @param  {String} offset  offset
         * @param  {String} opacity opacity
         * @return {Object}         渐变颜色点
         */
        createStop: function(root, color, offset, opacity) {
            var attrs = {
                offset: offset,
                "stop-color": color,
                "stop-opacity": opacity
            };

            return this.createElement("stop", attrs, root);
        }, 
        
        /**
         * 创建SVG元素对象
         *
         * 如果支持SVG就创建SVG节点；
         * 否则创建VML节点
         * 
         * @param  {String} tag       tag
         * @param  {Object} attr      attr
         * @param  {Object} container container
         * @return {Object}           svg element
         */
        createElement: function(tag, attr, container) {
            var settings = this._settings;
            var eTag;
            var vTagCross = {
                svg: "group",
                g: "group",
                path: "shape",
                circle: "oval"
            };
            if (this.isSVG()) {
                eTag = document.createElementNS(this.SVG_NS, tag);
            } else {
                if (vTagCross[tag]) {
                    eTag = document.createElement("<" + settings.V.NS + ":" + vTagCross[tag] + " >");
                    if (vTagCross[tag] == "group" || vTagCross[tag] == "shape") {
                        eTag.style.cssText = 'position:absolute;width:1px;height:1px;'; //grounp必须加上此样式才能正常显示
                        eTag.coordsize = "1,1";
                    }
                } else {
                    eTag = document.createElement("<" + settings.V.NS + ":" + tag + " >");
                }
            }

            if (attr && eTag) {
                this.setAttributes(eTag, attr);
            }

            if (container) container.appendChild(eTag);
            return eTag;
        },
        /**
         * 设置属性
         * 
         * @param  {Object} element       element
         * @param  {Object} attr      attr
         */
        setAttributes: function(element, attr) {
            if (!attr || !element) {
                return;
            }
            for (var key in attr) {
                if (this.isSVG()) {
                    if (attr.hasOwnProperty(key)) {
                        if (key.substring(0, 6) == "xlink:") {
                            element.setAttributeNS(this.XLINK_NS, key.substring(6), attr[key]);
                        } else {
                            element.setAttribute(key, attr[key]);
                        }
                    }
                } else {
                    //svg与vml对应属性
                    var vAttrCross = {
                        circle: "oval",
                        fill: "fillcolor",
                        stroke: "strokecolor",
                        "stroke-width": "strokeweight",
                        d: "path"
                    };
                    var keyA = vAttrCross[key] ? vAttrCross[key] : key;
                    if (element.tagName == "oval" && RE_OVAL.test(key)) {
                        ovalGetterAndSetter.set(element, key, attr[key]);
                    }else if (keyA == "opacity" && element.tagName != "fill" && element.tagName != "stroke") {
                        element.style.filter = "alpha(opacity=" + attr[key] * 100 + ")";
                    } else if (key == 'width' || key == 'height') {
                        $(element).attr(key, attr[key]);
                    }  else {
                        element[keyA] = attr[key];
                    }
                }
            }
        },
        /**
         * 获取属性
         * 
         * @param  {Object} element svg element
         * @param  {String} attr    attribute name
         * @return {Object}         attribute value
         */
        getAttribute: function(element, attr) {
            if (!attr) {
                return;
            }
            return element.getAttribute(attr);
        },
        /**
         * 设置动画参数
         * 
         * @param {Object} obj       svg element
         * @param {Object} beforeVal from value 动画需要修改的前属性值
         * @param {Object} endVal    to value 当前动画需要执行的属性
         * @param {String} attr      动画属性名称
         * @param {Number} num       标签元素中控制动画属性个数
         * @param {Number} [time = 0.5]      动画时间
         * @param {String} begin     动画开始时间设置 "" || "indefinite"
         */
        setAnimate: function(obj, beforeVal, endVal, attr, num, time, begin) {
            if (this.supportSMIL()) {
                var t = time || 0.5;
                var begin = begin || "indefinite";
                var _animate = this.createElement("animate", {
                    "attributeName": attr,
                    "begin": begin,
                    "dur": t + "s",
                    "fill": "freeze",
                    "from": beforeVal,
                    "to": endVal
                }, obj);
                if ($(obj).find("animate").length > num) {
                    $(obj).find("animate").eq(0).remove();
                }
                if (begin == "indefinite") {
                    _animate.beginElement();
                }
            }
        },
        /**
         * setDataMark
         * 
         * @param {Boolean} isVertical  柱子是否横向，是否是条形图
         * @param {Number}  datalen     数据序列长度
         * @param {Function}  onResetMark onResetMark(dot, index)
         * @param {Object}  type        "" || "create" || "modify"
         */
        setDataMark: function(isVertical, datalen, onResetMark, type) {
            var settings = this._settings;
            settings.root.append(settings.mainCord);
            settings.orientation = isVertical;
            settings.dataLength = datalen;
            var poses = this.getMainPoses(datalen, isVertical); //获取X坐标集
            var xSpaceWidth = poses[1] - poses[0];

            // 必须根据当前节点来计算，如果节点个数不等，那么必须重新创建节点
            if (settings.mainCord.find(".dot").length == datalen) {
                settings.mainCord.find(".dot").each(function(i) {
                    var $this = $(this);

                    if (typeof onResetMark == 'function') {
                        onResetMark($this, $this.attr('index'));
                    }
                    if ($this.outerWidth(true) > xSpaceWidth && settings.isLineFeed == "false") {
                        settings.isLineFeed = true;
                    }
                    _setPosMainCord(settings, $this, poses[i]);
                });
            } else {
                settings.mainCord.find(".dot").remove();
                settings.mainCord.find(".rule").remove();
                for (var i = 0; i < datalen; i++) {
                    var dot = $("<div class='dot'></div>").attr("index", i);

                    if (typeof onResetMark == 'function') {
                        onResetMark(dot, i, poses);
                    }
                    if (dot.text()) {
                        settings.cordText.append(dot);

                        if (dot.outerWidth(true) > xSpaceWidth && settings.isLineFeed == "false") {
                            settings.isLineFeed = true;
                        }
                        _setPosMainCord(settings, dot, poses[i]);

                        if (settings.isXRule && dot.text()) {
                            var rule = $("<div class='rule'></div>");
                            settings.cordRule.append(rule);
                            _setPosMainCord(settings, rule, poses[i], dot);
                            rule = null;
                        }
                    }
                    dot = null;
                }
            }
            var size = _calMaxItemSize(settings, settings.mainCord, settings.orientation);
            _setSizeMainCord(settings, size);
        },
        /**
         * add node.
         * 
         * @param {Number} data dot text
         * @param {Object} x    [description]
         */
        addNode: function(data, x) {
            var settings = this._settings;
            var dot = $("<div class='dot'></div>").text(data).appendTo(settings.cordText);
            var rule = $("<div class='rule'></div>").appendTo(settings.cordRule);

            _setPosMainCord(settings, dot, x);
            _setPosMainCord(settings, rule, x, dot);
            rule = null;
            delete rule;
        },
        /**
         * [removeNode description]
         * @param  {Number} index [description]
         */
        removeNode: function(index) {
            var settings = this._settings;
            settings.mainCord.find(".dot:eq(" + index + ")").remove();
            settings.mainCord.find(".rule:eq(" + index + ")").remove();
        },
        /**
         * if needed, set this after setDataMark
         * 
         * @param {Number} max         data中的最大值
         * @param {Array} data        数据序列，number数组
         * @param {Function} onResetMark onResetMark(dot, index)
         * @param {Number} min         data中的最小值
         */
        setYCord1: function(max, data, onResetMark, min) {
            var settings = this._settings;
            settings.maxY1 = max;
            settings.minY1 = min;
            settings.dataY1 = data;
            settings.onResetMarkY1 = onResetMark;

            settings.firstNumY1 = 0; //存储Y1值大于0的数据
            settings.root.append(settings.yCord1);
            settings.yCord1.append(settings.y1CordText, settings.y1CordRule);
            if (!(data instanceof Array)) {
                settings.yCord1.find(".dot").remove();
                return;
            }

            //以y轴刻度数组的第1个为起点
            if ( /*min==null && */ data[0] > 0) {
                settings.firstNumY1 = data[0];
            }
            //转换坐标
            var data1 = _getTall(settings, data, max, settings.orientation, min, settings.firstNumY1); //Y轴坐标集

            if (settings.yCord1.find(".dot").length == data.length) {
                settings.yCord1.find(".dot").each(function(i) {
                    if (typeof onResetMark == 'function') {
                        onResetMark($(this), i);
                    }
                    _setPosYCord1(settings, $(this), data1[i]);
                    if (settings.isYRuleInterlay) {
                        if (settings.orientation) {
                            settings.yCord1.find(".rule").eq(i * 2).height(settings.itemWidth + 5).width("auto");
                            settings.yCord1.find(".rule").eq(i * 2 + 1).height(settings.itemWidth + 2).width("auto");
                        } else {
                            settings.yCord1.find(".rule").eq(i * 2).width(settings.itemWidth + 5).height("auto");
                            settings.yCord1.find(".rule").eq(i * 2 + 1).width(settings.itemWidth + 2).height("auto");
                        }
                        _setPosYCord1(settings, settings.yCord1.find(".rule").eq(i * 2), data1[i], $(this));
                        _setPosYCord1(settings, settings.yCord1.find(".rule").eq(i * 2 + 1), data1[i], $(this), true, data1);
                    } else {
                        _setPosYCord1(settings, settings.yCord1.find(".rule").eq(i), data1[i], $(this));
                    }
                    if ((i == 0 || i == data.length - 1) && settings.isYRuleInterlay) {
                        if (!settings.orientation) {
                            var size = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
                            settings.yCord1.find(".rule").eq(i * 2).width((settings.itemWidth + 5) * 2).height("auto");
                        } else {
                            settings.yCord1.find(".rule").eq(i * 2).css({
                                "top": -(settings.itemWidth + 5)
                            }).height((settings.itemWidth + 5) * 2).width("auto");
                        }
                    }
                });
            } else {
                settings.yCord1.find(".dot").remove();
                settings.yCord1.find(".rule").remove();
                for (var i = 0; i < data.length; i++) {
                    var dot = $("<div class='dot'></div>").appendTo(settings.y1CordText);
                    if (typeof onResetMark == 'function') {
                        onResetMark(dot, i);
                    }
                    if (dot.text() == "") {
                        dot.css("padding", 0);
                    }
                    _setPosYCord1(settings, dot, data1[i]);

                    if (settings.isYRule) {
                        var rule = $("<div class='rule'></div>").appendTo(settings.y1CordRule);
                        if (settings.isYRuleInterlay) {
                            if (settings.orientation) {
                                rule.addClass("mainLine").height(settings.itemWidth + 5);
                                var rule2 = $("<div class='rule'></div>").appendTo(settings.y1CordRule).height(settings.itemWidth + 2);
                            } else {
                                rule.addClass("mainLine").width(settings.itemWidth + 5);
                                var rule2 = $("<div class='rule'></div>").appendTo(settings.y1CordRule).width(settings.itemWidth + 2);
                            }
                            _setPosYCord1(settings, rule2, data1[i], dot, true, data1);
                        }
                        _setPosYCord1(settings, rule, data1[i], dot);
                        if ((i == 0 || i == data.length - 1) && settings.isYRuleInterlay) {
                            if (settings.orientation) {
                                rule.height((settings.itemWidth + 5) * 2).css({
                                    "top": -(settings.itemWidth + 5)
                                });
                            } else {
                                rule.width((settings.itemWidth + 5) * 2);
                            }
                        }
                        rule = null;
                    }
                    dot = null;
                }
            }
            var size = _calMaxItemSize(settings, settings.yCord1, !settings.orientation);
            _setSizeYCord1(settings, size);
            _relayoutMainCord(settings, settings.dataLength);
        },
        /**
         * if needed, set this after setDataMark
         * 
         * @param {Number} max         data中的最大值
         * @param {Array} data        数据序列，number数组
         * @param {Function} onResetMark onResetMark(dot, index)
         * @param {Number} min         data中的最小值
         */
        setYCord2: function(max, data, onResetMark, min) {
            var settings = this._settings;
            settings.firstNumY2 = 0;
            settings.root.append(settings.yCord2);
            settings.yCord2.append(settings.y2CordText, settings.y2CordRule);
            //转换坐标
            var data1 = data; //
            if (!(data1 instanceof Array)) {
                settings.yCord2.find(".dot").remove();
                return;
            }
            //以y轴刻度数组的第1个为起点
            if (data[0] > 0) {
                settings.firstNumY2 = data[0];
            }
            if (!settings.orientation) {
                data1 = _getTall(settings, data, max, false, min, settings.firstNumY2);
            }
            if (settings.yCord2.find(".dot").length == data.length) {
                settings.yCord2.find(".dot").each(function(i) {
                    if (typeof onResetMark == 'function') {
                        onResetMark($(this), i);
                    }
                    _setPosYCord2(settings, $(this), data1[i]);
                    _setPosYCord2(settings, settings.yCord2.find(".rule").eq(i), data1[i], $(this));
                });
            } else {
                settings.yCord2.find(".dot").remove();
                settings.yCord2.find(".rule").remove();
                for (var i = 0; i < data.length; i++) {
                    var dot = $("<div class='dot'></div>");
                    settings.y2CordText.append(dot);
                    if (typeof onResetMark == 'function') {
                        onResetMark(dot, i);
                    }
                    _setPosYCord2(settings, dot, data1[i]);

                    if (settings.isYRule) {
                        var rule = $("<div class='rule'></div>");
                        settings.y2CordRule.append(rule);
                        _setPosYCord2(settings, rule, data1[i], dot);
                        rule = null;
                    }
                    dot = null;
                }
            }
            var size = _calMaxItemSize(settings, settings.yCord2, true);
            _setSizeYCord2(settings, size);
            if (!settings.orientation) {
                _relayoutMainCord(settings, settings.dataLength);
            } else {
                this.setYCord1(settings.maxY1, settings.dataY1, settings.onResetMarkY1, settings.minY1);
            }

        },
        /**
         * 获取绘图区的left, top, right, bottom
         * 
         * @param  {Boolean} isVertical [description]
         * @return {Object}             [description]
         */
        getDrawArea: function(isVertical) {
            return _getDrawArea(this._settings, isVertical);
        },
        /**
         * 获取图元X序列位置
         * 
         * @param  {Object}  datalen    [description]
         * @param  {Boolean} isVertical [description]
         * @return {Object}             [description]
         */
        getMainPoses: function(datalen, isVertical) {
            return _getMainPoses(this._settings, datalen);
        },
        /**
         * 获取图元尺寸和间隔
         * 
         * @param  {Object} datalen [description]
         * @return {Object}         [description]
         */
        getMainSize: function(datalen) {
            return _calSize(this._settings, datalen);
        },
        /**
         * Y轴数值转化使用。获取相对于坐标轴空间的像素值转化结果
         * 
         * @param  {Object} value    [description]
         * @param  {Object} max      [description]
         * @param  {Object} total    [description]
         * @param  {Object} min      [description]
         * @param  {Object} firstNum [description]
         * @return {Object}          [description]
         */
        getTransValue: function(value, max, total, min, firstNum) {
            return _transValue(this._settings, value, max, total, min, firstNum);
        },
        /**
         * 设置网格
         * 
         * @param {Object}  container   [description]
         * @param {Number}  max         [description]
         * @param {Array}  data        [description]
         * @param {Function}  onResetMark [description]
         * @param {Number}  min         [description]
         * @param {Boolean} isInterlay  [description]
         * @param {String}  type        "column" || ""
         * @param {Number}  xDataLength [description]
         */
        setXGrid: function(container, max, data, onResetMark, min, isInterlay, type, xDataLength) {
            var settings = this._settings;
            var self = this;
            container.append(settings.grid);
            var data1 = _getTall(settings, data, max, settings.orientation, min, settings.firstNumY1); //Y轴坐标集
            var top = (!settings.orientation && settings.isNoBeyond) ? (-settings.totalNum + 2 || 0) : 0;
            settings.grid.css({
                "top": top
            });
            var width = container.width();
            var height = container.height();
            if (settings.grid.find(".gLine").length == data.length - 1) {
                settings.grid.find(".gLine").each(function(i) {
                    if (!settings.orientation) {
                        $(this).removeAttr("style").css({
                            "top": data1[i + 1] - 3,
                            "width": width
                        });
                        if (i == settings.grid.find(".gLine").length - 1 && type != "column") {
                            $(this).css({
                                "border-top-style": "solid"
                            });
                        }
                    } else {
                        $(this).removeAttr("style").css({
                            "left": data1[i] - settings.mainCord.outerWidth(true) - 2,
                            "height": height
                        });
                        if (type == "column") {
                            var poses = self.getMainPoses(xDataLength, settings.orientation); //获取X坐标集
                            var xPace = poses[1] - poses[0];
                            $(this).css({
                                "top": poses[0] - xPace / 2,
                                "height": height - (poses[0] - xPace / 2) * 2 + 1
                            });
                        }
                        if (type == "column" && settings.yCord1.text() == "") {
                            var leftPace = (settings.root.outerWidth(true) - settings.mainCord.outerWidth(true)) / 4;
                            $(this).css({
                                "left": i * leftPace
                            });
                        }
                    }
                });
            } else {
                settings.grid.find(".gLine").remove();
                var dataLength = (type == "column" && settings.orientation && settings.yCord1.text() == "") ? 4 : data.length; //如果是柱状横向显示单独处理(当y轴没有数据时3条网格平分，否按y轴数据对齐)
                for (var i = 0; i < dataLength; i++) {
                    var num = data[0] > 0 ? -1 : 0;
                    if (i != num) {
                        var g = $("<div class='gLine'></div>").appendTo(settings.grid);
                        var g2;
                        if (isInterlay) {
                            g.addClass("mainLine");
                            g2 = $("<div class='gLine'></div>").appendTo(settings.grid);
                        }
                        if (!settings.orientation) {
                            g.css({
                                "top": data1[i] - 2,
                                "width": width
                            });
                            if (isInterlay) {
                                g2.css({
                                    "top": data1[i] + ((data1[i - 1] - data1[i] - 2) / 2),
                                    "width": width
                                });
                            }
                            if (i == dataLength - 1 && type != "column") {
                                g.css({
                                    "border-top-style": "solid"
                                });
                            }
                        } else {
                            g.css({
                                "left": data1[i] - settings.mainCord.outerWidth(true) - 1,
                                "height": height
                            });
                            if (type == "column") {
                                var poses = this.getMainPoses(xDataLength, settings.orientation); //获取X坐标集
                                var xPace = poses[1] - poses[0];
                                g.css({
                                    "top": poses[0] - xPace / 2,
                                    "height": height - (poses[0] - xPace / 2) * 2 + 1
                                });
                            }
                            if (type == "column" && settings.yCord1.text() == "") {
                                var leftPace = (settings.root.outerWidth(true) - settings.mainCord.outerWidth(true)) / 4;
                                g.css({
                                    "left": i * leftPace
                                });
                            }
                            if (isInterlay && type != "column") {
                                g2.css({
                                    "left": data1[i] + ((data1[i - 1] - data1[i]) / 2 - settings.mainCord.outerWidth(true) - 1),
                                    "height": height
                                });
                            }
                        }
                        if (settings.y1CordText.children(".dot:eq(" + i + ")").text() == "") {
                            g.hide();
                            if (isInterlay) {
                                g2.hide();
                            }
                            //g2.hide();	
                        }
                    }
                }
            }
        },
        /**
         * [setYGrid description]
         * @param {Object} container [description]
         * @param {Number} count     [description]
         * @param {String} type      "" || "column"
         */
        setYGrid: function(container, count, type) {
            var settings = this._settings;
            container.append(settings.gridY);

            var poses = this.getMainPoses(count, settings.orientation); //获取X坐标集
            var top = (!settings.orientation && settings.isNoBeyond) ? (-settings.totalNum + 2 || 0) : 0;
            settings.grid.css({
                "top": top
            });
            var width = container.width();
            var height = container.height();
            if (settings.gridY.find(".gLine").length == count) {
                settings.gridY.find(".gLine").each(function(i) {
                    if (!settings.orientation) {
                        $(this).removeAttr("style").css({
                            "left": poses[i] - 1,
                            "height": height
                        });
                    } else {
                        if (type == "column") {
                            var xPace = poses[1] - poses[0];
                            $(this).removeAttr("style").css({
                                "top": (poses[i] - 1) - xPace / 2,
                                "width": width
                            });
                            if (i == count - 1) {
                                var gLast = $("<div class='gLine'></div>").appendTo(settings.gridY);
                                gLast.css({
                                    "top": (poses[i] - 1) + xPace / 2,
                                    "width": width
                                });
                            }
                        } else {
                            $(this).removeAttr("style").css({
                                "top": poses[i] - 1,
                                "width": width
                            });
                        }
                    }
                });
            } else {
                settings.gridY.find(".gLine").remove();

                for (var i = 0; i < count; i++) {
                    var g = $("<div class='gLine'></div>").appendTo(settings.gridY);
                    if (i % 2 != 0) {
                        g.addClass("mainLine");
                    }

                    if (!settings.orientation) {
                        g.css({
                            "left": poses[i] - 1,
                            "height": height
                        });
                    } else {
                        if (type == "column") {
                            var xPace = poses[1] - poses[0];
                            g.css({
                                "top": (poses[i] - 1) - xPace / 2,
                                "width": width
                            });
                            if (i == count - 1) {
                                var gLast = $("<div class='gLine'></div>").appendTo(settings.gridY);
                                gLast.css({
                                    "top": (poses[i] - 1) + xPace / 2,
                                    "width": width
                                });
                            }
                        } else {
                            g.css({
                                "top": poses[i] - 1,
                                "width": width
                            });
                        }
                    }
                }
            }
        },
        /**
         * if needed, set this before setDataMark
         * 
         * @param {Object} data       [description]
         * @param {Object} columnNum  兼容图例多时，以多列展现(饼图)
         * @param {Object} alarmLevel [description]
         */
        setLegend: function(data, columnNum, alarmLevel) {
            var settings = this._settings;
            settings.legendArray = []; //存储图例
            settings.maxLegendWidth = 0;
            settings.legendPad = 20;
            var circleWidth = 20;
            settings.textFill = ucd.getStyle() == "gray" ? "#9E9E9E" : "none";
            if (settings.legend == null) {
                settings.legend = $("<div class='legend'></div>");
                settings.root.append(settings.legend);
                if (this.isSVG()) {
                    settings.svgLegend = this.createElement("svg", {
                        version: "1.1"
                    }, settings.legend[0]);
                    $(settings.svgLegend).css({
                        "position": "absolute",
                        "width": "100%",
                        "height": "100%"
                    });
                    if (columnNum == "undefined") {
                        $(settings.svgLegend).css({
                            "height": "auto"
                        });
                    }
                } else {
                    settings.svgLegend = $("<div></div>").css("position", "absolute").appendTo(settings.legend)[0];
                }
            }
            var itemConArray = [];
            var legendWidth = 0;
            settings.leWidth = 0;
            var colors = ucd.getColors(data.length);
            if (alarmLevel) {
                colors = ucd.getAlarmColors(alarmLevel);
            }

            var legendImgWidth = 15;
            var legendY = 0;
            for (var i = 0; i < data.length; i++) {
                var color = colors[i] || settings.defaultColor;
                var itemCon;
                var items = {};
                var x1 = 10;
                var y = 10;
                if ((columnNum && columnNum > 0 && i == 0) || i % columnNum == 0) {
                    if (itemConArray.length > 0) {
                        settings.leWidth += this.isSVG() ? itemConArray[itemConArray.length - 1].getBBox().width + 10 : circleWidth + settings.maxLegendWidth;
                    }
                    legendY = 0;
                    itemCon = this.createElement("g", {
                        "transform": "translate(" + settings.leWidth + "," + (0) + ")"
                    }, settings.svgLegend);
                    if (!this.isSVG()) {
                        $(itemCon).css({
                            "left": settings.leWidth
                        });
                    }
                    itemConArray.push(itemCon);
                    settings.maxLegendWidth = 0;
                }

                if (columnNum && columnNum > 0) {
                    items.gLegend = this.createElement("g", {}, itemCon);
                } else {
                    items.gLegend = this.createElement("g", {}, settings.svgLegend);
                }
                $(items.gLegend).attr("index", i);

                var fill = ucd.getStyle() == "gray" ? "#FFF" : "none";
                items.circle = this.createElement("circle", {
                    "cx": x1,
                    "cy": y,
                    "r": 8,
                    "fill": fill,
                    "stroke": color,
                    "stroke-width": 2,
                    "cursor": "pointer"
                }, items.gLegend);
                var cPosition = x1;
                items.circleSelect = this.createElement("circle", {
                    "cx": cPosition,
                    "cy": cPosition,
                    "r": 4,
                    "fill": color,
                    "stroke": color,
                    "stroke-width": 2,
                    "cursor": "pointer"
                }, items.gLegend);
                $(items.circleSelect).css({
                    "opacity": 0
                });
                if (this.isSVG()) {
                    items.text = this.createElement("text", {
                        "x": x1 + legendImgWidth,
                        "font-size": 12,
                        "cursor": "pointer",
                        "fill": "#9E9E9E",
                        "text-anchor": "start"
                    }, items.gLegend);
                    items.text.textContent = data[i];
                } else {
                    items.text = $("<span></span>").css({
                        "position": "absolute",
                        "left": $(items.circle).width() + settings.leWidth,
                        "padding-left": legendImgWidth,
                        "font-size": 12,
                        "cursor": "pointer",
                        "color": "#9E9E9E",
                        "white-space": "nowrap"
                    }).text(data[i]).appendTo(items.gLegend);
                    items.text.css({
                        "top": (items.text.height() / 2) - 2
                    });
                }
                if (i == data.length - 1 && itemCon) {
                    settings.leWidth += this.isSVG() ? itemCon.getBBox().width + 10 : settings.maxLegendWidth + circleWidth;
                }
                var textHeight = this.isSVG() ? items.text.getBBox().height : $(items.text).height();
                var gLegendHeight = this.isSVG() ? items.gLegend.getBBox().height : $(items.gLegend).height();
                this.setAttributes(items.text, {
                    "y": y + parseInt(textHeight / 3)
                });

                items.rectLegend = this.createElement("rect", {
                    width: x1 + legendImgWidth,
                    height: circleWidth,
                    x: x1,
                    y: 0,
                    "fill-opacity": "0.000001",
                    "cursor": "pointer",
                    "strokecolor": "none"
                }, items.gLegend);
                if (columnNum >= 0 && columnNum != null) {
                    this.setAttributes(items.gLegend, {
                        "transform": "translate(" + 0 + "," + (legendY) + ")"
                    });
                    if (!this.isSVG()) {
                        $(items.gLegend).css({
                            "top": legendY
                        });
                    }
                    legendY += textHeight + 7;
                } else {
                    this.setAttributes(items.gLegend, {
                        "transform": "translate(" + legendWidth + "," + 0 + ")"
                    });
                    if (!this.isSVG()) {
                        $(items.gLegend).css({
                            "left": legendWidth
                        });
                    }
                }

                items.width = this.isSVG() ? items.gLegend.getBBox().width : $(items.text).width() + $(items.circle).width();
                legendWidth += items.width + settings.legendPad;
                if (items.width > settings.maxLegendWidth) {
                    settings.maxLegendWidth = items.width;
                }
                settings.legendArray.push(items);
            }
            if (settings.leWidth != 0) {
                settings.legend.width(settings.leWidth);
            }
            if (columnNum == 0) {
                settings.legend.width(settings.maxLegendWidth + 10);
            }
            if (columnNum == "undefined" || columnNum == null) {
                settings.legend.width("100%");
            }

        },
        /**
         * 设置图例over/out事件
         *
         * @event UCD.Cords#setLegendOnHover
         * 
         * @param {Object} settings [description]
         * @param {Object} over     [description]
         * @param {Object} out      [description]
         */
        setLegendOnHover: function(settings, over, out) {
            settings.legendOnHover = over ? over : settings.legendOnHover;
            settings.legendOnOut = out ? out : settings.legendOnOut;
            if (settings.Source.isPad) {
                return;
            }
            if (this._settings.legendArray.length > 0) {
                for (var i = 0; i < this._settings.legendArray.length; i++) {
                    $(this._settings.legendArray[i].gLegend).unbind("mouseover").mouseover(function() {
                        settings.self._legendFire(settings, $(this), settings.legendOnHover, "select");
                    });
                    $(this._settings.legendArray[i].gLegend).unbind("mouseout").mouseout(function() {
                        var state = $(this).attr("state");
                        if (!state) {
                            settings.self._legendFire(settings, $(this), settings.legendOnOut, "unselect");
                        }
                    });
                }
            }
        },
        /**
         * 设置图例click事件
         *
         * @event UCD.Cords#setLegendOnClick
         * 
         * @param {Object}   settings [description]
         * @param {Function} fn       [description]
         */
        setLegendOnClick: function(settings, fn) {
            var _settings = this._settings;
            settings.legendOnClick = fn ? fn : settings.legendOnClick;
            if (_settings.legendArray.length > 0) {
                for (var i = 0; i < _settings.legendArray.length; i++) {
                    $(_settings.legendArray[i].gLegend).unbind(_settings.Source.CLICK).bind(_settings.Source.CLICK, function() {
                        var state = $(this).attr("state");
                        if (!state) {
                            settings.self._legendFire(settings, $(this), settings.legendOnClick, "select");
                            $(this).attr("state", "click");
                        } else {
                            $(this).removeAttr("state");
                            settings.self._legendFire(settings, $(this), settings.legendOnClick, "unselect");
                        }
                        if (settings.itemsArray[$(this).index()]) {
                            $(settings.itemsArray[$(this).index()].line).removeAttr("state");
                        }
                    });
                }
            }
        },
        /**
         * [getLegendWidth description]
         * 
         * @return {Number} [description]
         */
        getLegendWidth: function() {
            var width = 0;
            if (this._settings.legend) {
                width = this._settings.legend.width();
            }
            return width;
        },
        /**
         * 设置目标线
         * 
         * @param {Object}  settings   [description]
         * @param {Number}  value      [description]
         * @param {Object}  dot        [description]
         * @param {String}  color      [description]
         * @param {Boolean} isHideDot  是否隐藏目标圆点
         * @param {Boolean} isHideLine 是否隐藏目标线
         */
        setGoal: function(settings, value, dot, color, isHideDot, isHideLine) {
            var isDotPos = $.isNumeric(dot);

            if (!settings.extend) {
                settings.extend = $("<div class='extend'></div>");
                settings.root.append(settings.extend);
            }
            var flag = false;
            settings.extend.children(".goal").each(function(index, element) {
                if ($(this).attr("goal") == value) {
                    flag = true;
                }
            });
            if (!flag) {
                var item = $("<div class='goal'><div class='dot'></div><div class='line'></div></div>").attr("goal", value);
                if (color) {
                    $(".line", item).css({
                        "border-color": color
                    });
                    $(".dot", item).css({
                        "background-color": color
                    });
                    if (isHideDot) {
                        $(".dot", item).remove();
                    }
                    if (isHideLine) {
                        $(".line", item).remove();
                    }
                }
                settings.extend.append(item);
                var doth = parseInt(item.find(".dot").width() * 0.5 - 0.5);
                var frags = settings.isComPlex == true ? settings.main : settings.frags;
                var goalLinePos;

                if (!settings.orientation) {// 水平
                    if (isDotPos) {
                        goalLinePos = dot;
                    } else {
                        goalLinePos = parseInt(dot.get(0).style.top) + Math.round(dot.outerHeight() * 0.5);
                    }

                    var left = this._settings.yCord1.width();
                    item.css("left", left - doth)
                        .css("top", goalLinePos - doth - 1)
                        .width(frags.width() + doth).height(doth * 2);
                    item.children(".line").css("top", doth).css("left", "");
                } else {
                    if (isDotPos) {
                        goalLinePos = dot;
                    } else {
                        goalLinePos = parseInt(dot.get(0).style.left) + dot.outerWidth(true) / 2;
                    }

                    item.css("left", goalLinePos + this._settings.mainCord.width() - doth)
                        .css("top", 0)
                        .height(frags.height() + doth).width(doth * 2);
                    item.children(".dot").css("bottom", 0).css("top", "");
                    item.children(".line").css("left", doth).css("top", "");
                }
                !isDotPos && dot.attr("extend", true);
            }
        },
        /**
         * [setFragsSize description]
         * 
         * @param {Object} settings [description]
         */
        setFragsSize: function(settings) {
            var size = this.getDrawArea(settings.orientation);
            if (settings.isComPlex) {
                size = {
                    "left": 0,
                    "bottom": 0,
                    "right": 0,
                    "top": "0"
                };
            }
            var legendHeight = this._settings.legend ? this._settings.legend.height() : 0;

            if (!settings.orientation && this._settings.isNoBeyond) {
                var mainH = !settings.orientation ? this._settings.mainCord.height() : this._settings.yCord1.height();
                if (!settings.isComPlex) {
                    var h = settings.container.height() - mainH - legendHeight;
                    settings.frags.css({
                        "left": size.left,
                        "bottom": size.bottom,
                        "right": size.right,
                        "top": h - (Math.round(h * this._settings.totalRatio) + 1)
                    });
                } else {
                    settings.frags.css({
                        "left": size.left,
                        "bottom": size.bottom,
                        "right": size.right,
                        "top": size.top
                    });
                }
            } else {
                settings.frags.css({
                    "left": size.left,
                    "bottom": size.bottom,
                    "right": size.right,
                    "top": size.top
                });
            }

            if (settings.fragsZoom) {
                settings.fragsZoom.css({
                    "left": size.left,
                    "bottom": size.bottom,
                    "right": size.right,
                    "top": size.top
                });
            }
        },
        /**
         * 选中主轴上对应索引的标签
         * 
         * @param  {Object} index [description]
         * @return {Object}       [description]
         */
        select: function(index) {
            var settings = this._settings;
            this.unselect();
            settings.mainCord.find(".dot:eq(" + index + ")").addClass("selected");
            if (settings.orientation) {
                settings.yCord2.children(".dot:eq(" + index + ")").addClass("selected");
            }
        },
        /**
         * [unselect description]
         * 
         * @return {Object} [description]
         */
        unselect: function() {
            var settings = this._settings;
            settings.mainCord.find(".dot.selected").removeClass("selected");
            if (settings.orientation) {
                settings.yCord2.children(".dot.selected").removeClass("selected");
            }
        },
        /**
         * 选择Y轴
         * 
         * @param  {Object} y     [description]
         * @param  {Object} color [description]
         * @return {Object}       [description]
         */
        selectCord: function(y, color) {
            var settings = this._settings;
            var cord = y == "y1" ? settings.yCord1 : settings.yCord2;
            cord.find(".dot").each(function(index) {
                var extend = $(this).attr("extend");
                if (!extend) {
                    $(this).css({
                        "color": color
                    });
                    cord.find(".rule:eq(" + index + ")").css({
                        "border-color": color
                    });
                }
            });
        },
        /**
         * 取消选中Y轴
         * 
         * @param  {String} y null || "y1"
         */
        unselectCord: function(y) {
            var settings = this._settings;
            var color = ucd.getStyle() == "black" ? "#C6C6C6" : "#9E9E9E";
            if (!y) {
                settings.yCord1.find(".dot").css({
                    "color": color
                });
                settings.yCord1.find(".rule").css({
                    "border-color": color
                });
                settings.yCord2.find(".dot").css({
                    "color": color
                });
                settings.yCord2.find(".rule").css({
                    "border-color": color
                });
            } else {
                var cord = (y == "y1") ? settings.yCord1 : settings.yCord2;
                cord.find(".dot").css({
                    "color": color
                });
                cord.find(".rule").css({
                    "border-color": color
                });
            }
        },
        /**
         * 是否支持CSS3
         *
         * TODO: 判断不全，应该根据caniuse上面的数据库，根据CSS3属性名称来判断。应该根据功能来判断，这样才能优雅降级。
         * 
         * @return {Boolean}
         */
        isValidCSS3: function() {
            // if ($.browser.webkit || ($.browser.msie && parseFloat($.browser.version) >= 10)) {
            //     return true;
            // }
            return true;
        },
        /**
         * 修复最大值/最小值
         *
         * @private
         * 
         * @param  {Object} settings [description]
         * @param  {Array} data     [description]
         * @param  {Number} max      [description]
         * @param  {Number} min      [description]
         * @return {Object}          {min, max}
         */
        repairMaxMin: function(settings, data, max, min) {
            if (data) {
                for (var i = 0; i < data.length; i++) {
                    if (max < data[i]) {
                        max = data[i];
                    }
                    if (min > data[i]) {
                        min = data[i];
                    }
                }
            }
            var items = {
                max: max,
                min: min
            };
            return items;
        },
        /**
         * 获取第一色调值
         * 
         * @param  {Number} length 长度
         */
        getMainColor: function(length) {
            var color = {
                1: 1,
                2: 1,
                3: 2,
                4: 3,
                6: 4,
                8: 5,
                13: 8,
                20: 12
            }
            return color[length];
        },
        /**
         * 是否显示Y轴刻度
         *
         * @param  {Boolean} flag flag
         */
        enableYRule: function(flag) {
            this._settings.isYRule = flag;
        },
        /**
         * 是否显示X轴刻度
         *
         * @param  {Boolean} flag flag
         */
        enableXRule: function(flag) {
            this._settings.isXRule = flag;
        },
        /**
         * 是否显示Y轴标尺
         * 
         * @param  {Boolean} flag flag
         * @param  {String} orientation orientation
         */
        enableYRuleLine: function(flag, orientation) {
            this._settings.isYRuleLine = flag;
            var px = flag ? "1px" : "0px";
            var obj = this._settings.container.find(".main").length > 0 ? this._settings.container.find(".main") : this._settings.container.find(".Frags");
            if (orientation) {
                obj.css({
                    "border-bottom-width": px,
                    "border-right": "none"
                });
            } else {
                obj.css({
                    "border-left-width": px,
                    "border-right": "none"
                });
            }
        },
        /**
         * 是否显示X轴标尺
         * 
         * @param  {Boolean} flag flag
         * @param  {String} orientation orientation
         */
        enableXRuleLine: function(flag, orientation) {
            this._settings.isXRuleLine = flag;
            var px = flag ? "1px" : "0px";
            var obj = this._settings.container.find(".main").length > 0 ? this._settings.container.find(".main") : this._settings.container.find(".Frags");
            if (orientation) {
                obj.css({
                    "border-left-width": px,
                    "border-top": "none"
                });
            } else {
                obj.css({
                    "border-bottom-width": px,
                    "border-top": "none"
                });
            }
        },
        /**
         * 是否贴边显示
         * 
         * @param  {Boolean} flag flag
         */
        enableSide: function(flag) {
            this._settings.isSide = flag;
        },
        /**
         * 允许拖拽
         * 
         * @param  {Object} settingsObj      对象集合
         * @param  {Number} xSpaceWidth      X轴之间间距
         * @param  {Object} frags            拖拽对象
         * @param  {Object} setObj1          操作对象
         * @param  {Object} setObj2          操作对象
         * @param  {Object} onDragMark       [description]
         * @param  {Object} onDragBeforeMark [description]
         */
        enableDrag: function(settingsObj, xSpaceWidth, frags, setObj1, setObj2, onDragMark, onDragBeforeMark) {
            var self = this;
            var settings = this._settings;
            if (settingsObj.isDrag) {
                settings.mainCord.css({
                    "overflow": "hidden"
                });
                settings.frags.css({
                    "overflow": "hidden"
                });
                if (settingsObj.main) {
                    settingsObj.main.css({
                        "overflow": "hidden"
                    });
                }

                if (settings._enableDrag == settingsObj.isDrag) {
                    return;
                }
                var startX = 0;
                var objLeft = 0;
                frags.bind(settings.Source.MOUSEDOWN, function(e) {
                    hidCount = settings.initDataCount ? settingsObj.data.length - settings.initDataCount : 0;
                    hidCount = settings.isSide ? hidCount + 1 : hidCount;
                    startX = !settings.Source.isPad ? e.pageX : event.targetTouches[0].pageX;
                    befordrag(e);
                    if (onDragBeforeMark && typeof onDragBeforeMark == "function") {
                        onDragBeforeMark()
                    }
                });
                //开始拖拽
                function befordrag(e) {
                        e.stopPropagation();
                        e.preventDefault();

                        objLeft = settings.scrollLeft || 0;
                        $(document).bind(settings.Source.MOUSEMOVE, ondrag).bind(settings.Source.MOUSEUP, afterdrop);
                    }
                    //拖拽中
                function ondrag(e) {
                        e.preventDefault();
                        e.stopPropagation();
                        var x = !settings.Source.isPad ? e.pageX : event.targetTouches[0].pageX;
                        var scrollLeft = objLeft + (x - startX);
                        if (onDragMark && typeof onDragMark == "function") {
                            onDragMark(scrollLeft)
                        }
                        var num = settingsObj.isSide ? 10 : (-settingsObj.padding || 0);
                        if (scrollLeft > num) {
                            scrollLeft = num;
                        }
                        if (Math.abs(scrollLeft) > hidCount * xSpaceWidth) {
                            scrollLeft = -hidCount * xSpaceWidth == 0 ? (-settingsObj.padding || 0) : -hidCount * xSpaceWidth;
                        }
                        settings.scrollLeft = scrollLeft;
                        frags.attr("leftAttr", settings.scrollLeft);
                        if (setObj1) {
                            setObj1.css({
                                "position": "absolute",
                                "left": settings.scrollLeft,
                                "-webkit-transition": "none"
                            });
                        }
                        if (setObj2) {
                            setObj2.css({
                                "position": "absolute",
                                "left": settings.scrollLeft,
                                "-webkit-transition": "none"
                            });
                        }
                        settings.cordContent.removeAttr("style").css({
                            "left": settings.scrollLeft + (settingsObj.padding || 0)
                        });
                        if (settingsObj.aimRectArray && settingsObj.aimRectArray.length > 0) {
                            for (var i = 0; i < settingsObj.aimRectArray.length; i++) {
                                self.setAttributes(settingsObj.aimRectArray[i].rect, {
                                    x: -settings.scrollLeft
                                })
                            }
                        }
                    }
                    //拖拽结束
                function afterdrop() {
                    $(document).unbind(settings.Source.MOUSEMOVE, ondrag).unbind(settings.Source.MOUSEUP, afterdrop);
                }
            } else {
                if (settings._enableDrag == isDrag) {
                    return;
                }
                settingsObj.frags.unbind(settings.Source.MOUSEDOWN);
            }
            settings._enableDrag = settingsObj.isDrag;
        },
        /**
         * ie兼容单独处理Path
         * 
         * @param  {Object} settings settings
         * @param {Object} obj       svg element
         * @param {String} path      path string 
         * @param {String} stroke    stroke color
         */
        IECom: function(settings, obj, path, stroke) {
            path = _parsePathString(path);

            var attrs = {
                x: 0,
                y: 0,
                bx: 0,
                by: 0,
                X: 0,
                Y: 0,
                qx: null,
                qy: null
            };
            for (var i = 0; i < path.length; i++) {
                path[i] = _processPath(settings, path[i], attrs);
                var seg = path[i],
                    seglen = seg.length;
                attrs.x = seg[seglen - 2];
                attrs.y = seg[seglen - 1];
                attrs.bx = parseFloat(seg[seglen - 4]) || attrs.x;
                attrs.by = parseFloat(seg[seglen - 3]) || attrs.y;
            }

            res = [];
            for (var j = 0; j < path.length; j++) {
                p = path[j];
                r = path[j][0].toLowerCase();
                for (var k = 1; k < p.length; k++) {
                    r += Math.round(p[k] * 21600) + (k != p.length - 1 ? "," : "");
                }
                res.push(r);
            }
            var stroke = stroke ? stroke : "none";
            settings.cord.setAttributes(obj, {
                "coordsize": "21600,21600",
                "d": res.join(" "),
                "stroke": stroke
            });
        },
        /**
         * 获取数据集个数
         * 
         * @param  {Object} settings settings
         * @return {Number}          根据data属性计算数据集个数
         */
        getValueLength: function(settings) {
            var valueLength1 = 0;
            for (var i = 0; i < settings.data.length; i++) {
                if (settings.data[i].value && settings.data[i].value.length > valueLength1) {
                    valueLength1 = settings.data[i].value.length;
                }
            }
            return valueLength1;
        }
    };
})(UCD, UCD.Core);
